﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KSASupport;
using System.Windows;
using KSASupport.BlobTracking;

namespace KSATouch
{
    public enum ContactStates {ContactUnknown,ContactDown,ContactUp,ContactMoved}
    public enum ContactType {Unknown,Finger,Object,Hand }

    /// <summary>
    /// A superclass of depthblob, a contact represents a finger, hand, or object which is detected. 
    /// </summary>
    public class Contact : DepthBlob
    {
        public static int CurrentContactID = 0;

        private DepthBlob contactBlob;
        /// <summary>
        /// The 'blob' data which this contact originated from
        /// </summary>
        public DepthBlob ContactBlob
        {
            get { return contactBlob; }
            set { contactBlob = value; }            
        }
       
        private DepthBlob closestItem;
        /// <summary>
        /// Represents the closest 'blob' to the contact...be it a hand, an object, etc.
        /// </summary>
        public DepthBlob ClosestItem
        {
            get { return closestItem; }
            set { closestItem = value; }
        }

        private DepthManager depthManager;
        /// <summary>
        /// Provide a handle to the depthManager which this contact was formed from. We can query it later for
        /// depth data to calcualate height/angle/etc. 'on the fly'
        /// </summary>
        public DepthManager DepthManager
        {
            get { return depthManager; }
            set { depthManager = value; }
        }
 
        private Contact previousPosition;
        /// <summary>
        /// The previous position of this contact, used for directional calculations
        /// </summary>
        public Contact PreviousPosition
        {
            get { return previousPosition; }
            set { previousPosition = value; }
        }

        private int contactZHeight;
        /// <summary>
        /// Tracks how far a contact is 'off' the screen. Useful for 'hovering' calculations
        /// </summary>
        public int ContactZHeight
        {
            get 
            {                
                int intitialValue = GetHeightFromPoint(this.contactBlob.CenterPoint);                
                // Try to filter this (NOTE: Cant filter unless we track the hovering ID's!)
                //contactZHeight = (int)InputFilter.GetInstance().GetFilteredValue("ContactHeight" + this.Id, contactZHeight);
                contactZHeight = intitialValue; 
                return intitialValue;
            }
        }

        /// <summary>
        /// Hand up the side of the table which this contact originated from. Hide inherited member
        /// </summary>
        public new BlobSide Side
        {
            get { return this.contactBlob.Side; }
        }

        private double yTilt;
        /// <summary>
        /// YTilt represents the tilt of a hovering object along the Y axes
        /// </summary>
        public double YTilt
        {
            get 
            {
                double yTiltRaw = CalculateYTilt();
                yTilt = InputFilter.GetInstance().GetFilteredValue("ContactYTilt" + this.Id, yTiltRaw);
                return yTilt; 
            }
        }

        private double xTilt;
        /// <summary>
        /// XTilt represents the tilt of a hovering object along the XAxis
        /// </summary>
        public double XTilt
        {
            get
            {
                double xTiltRaw = CalculateXTilt();
                xTilt = InputFilter.GetInstance().GetFilteredValue("ContactXTilt" + this.Id, xTiltRaw);
                return xTilt;
            }
        }

        internal int lifetime;
        
        

        private int GetHeightFromPoint(Point p)
        {
            int index = (int)p.Y * depthManager.DepthImageWidth + (int)p.X;
            return depthManager.CalibratedDepthValues[index];
        }

        /// <summary>
        /// Gets the 'tilt' value of the contact on the Y axis (side to side). Experimental..
        /// </summary>
        public double CalculateYTilt()
        {
            int centerBoundingHeight = (int)this.contactBlob.CenterPoint.Y;
            var points = this.contactBlob.Points.Where(pt => pt.Y == centerBoundingHeight);
            if (points.Count() == 0)
            {
                return 0;
            }
            Point minPoint = points.Where(pt=>pt.X == points.Select(p => p.X).Min()).First();
            Point maxPoint = points.Where(pt => pt.X == points.Select(p => p.X).Max()).First();
            int rightHeight = GetHeightFromPoint(minPoint);
            int lefttHeight = GetHeightFromPoint(maxPoint);
            double distance = BlobTracker.GetDistance(minPoint, maxPoint); // hypoteneuse of upcoming calculation
            int Opposite = rightHeight - lefttHeight;
            // Sin Theta = Opposite / Hypoteneuse :)            
            if (Opposite != 0)
            {
                // treat minpoint as 0,0, then use distance/height, and get the angle between them
                double radians = Math.Atan((0 - distance) / (0 - Opposite));
                double angle = radians * (180 / Math.PI);
                // We normalize this (similar to what we do with height), so that the developer that consumes this data 
                // gets back something that kinda makes sense.
                if (angle > 0)
                {
                    angle -= 90;
                }
                else
                {
                    angle += 90;
                }
                return angle;
            }
            else
            {
                return 0;
            }
        }

         /// <summary>
        /// Gets the 'tilt' value of the contact along the 'up/down' axis. experimental
        /// </summary>
        public double CalculateXTilt()
        {
            int centerBoundingHeight = (int)this.contactBlob.CenterPoint.X;
            var points = this.contactBlob.Points.Where(pt => pt.X == centerBoundingHeight);
            if (points.Count() == 0)
                return 0;
            Point minPoint = points.Where(pt=>pt.Y == points.Select(p => p.Y).Min()).First();
            Point maxPoint = points.Where(pt => pt.Y == points.Select(p => p.Y).Max()).First();
            int topHeight = GetHeightFromPoint(minPoint);
            int bottomHeight = GetHeightFromPoint(maxPoint);
            double distance = BlobTracker.GetDistance(minPoint, maxPoint); // hypoteneuse of upcoming calculation
            int Opposite = topHeight - bottomHeight;        
            if (Opposite != 0)
            {
                // treat minpoint as 0,0, then use distance/height, and get the angle between them
                double radians = Math.Atan((0 - distance) / (0 - Opposite));
                double angle = radians * (180 / Math.PI);
                // We normalize this (similar to what we do with height), so that the developer that consumes this data 
                // gets back something that kinda makes sense.
                if (angle > 0)
                {
                    angle -= 90;
                }
                else
                {
                    angle += 90;
                }
                return angle;
            }
            else
            {
                return 0;
            }
        }
        private ContactStates contactState;

        /// <summary>
        /// Tracks if the contact is up/down/or moving
        /// </summary>
        public ContactStates ContactState
        {
            get { return contactState; }
            set { contactState = value; }
        }

        public Contact(DepthManager manager)
        {
            Points = new List<Point>();
            this.depthManager = manager;
        }
        /// <summary>
        /// A contact is a blob which has been detected to be an actual finger/hand/arm/object on/over the screen. It gets 
        /// created, analyzed, categorized, and passed up with events in this library.
        /// </summary>
        /// <param name="blob"></param>
        //public Contact(DepthBlob blob,DepthManager manager)
        public Contact(DepthBlob blob)
        {
            CurrentContactID++;
            // Store off the blob that turned into this contact in case we want to 
            // query its values later (such as points, bounding rect. etc)
            this.Id = blob.Id;
            this.contactBlob = blob;
            this.Points.AddRange(blob.Points);           
        }
    }
}
